package com.pitaya.avatar.service.impl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.pitaya.avatar.domain.Area;
import com.pitaya.avatar.domain.Circle;
import com.pitaya.avatar.domain.POI;
import com.pitaya.avatar.global.Constants;
import com.pitaya.avatar.service.IClusterNode;
import com.pitaya.avatar.service.IClusterService;
import com.pitaya.avatar.util.KruskalCluster;

public class ClusterService implements IClusterService {

	private KruskalCluster clusterWorker; 
	
	private static double EARTH_RADIUS = 6378.137;
	
	private static double rad(double d)
	{
	   return d * Math.PI / 180.0;
	}
	
	public KruskalCluster getClusterWorker(){
		return this.clusterWorker;
	}
	
	public void setClusterWorker(KruskalCluster kc){
		this.clusterWorker = kc;
	}
	
	public static double getDistance(double lng1, double lat1, double lng2, double lat2)
	{
	   double radLat1 = rad(lat1);
	   double radLat2 = rad(lat2);
	   double a = radLat1 - radLat2;
	   double b = rad(lng1) - rad(lng2);
	   double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) + 
	    Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
	   s = s * EARTH_RADIUS;
	   //s = Math.round(s * 10000) / 10000;
	   return s;
	}		

	public Area getNodeArea(ArrayList<IClusterNode> nodes, String area_type) {
		Area area = null;
		if(nodes == null || nodes.size() ==0)
			return area;				
		double max_x = 0.0, max_y = 0.0, min_x = 0.0, min_y = 0.0;
		double center_x = 0.0;
		double center_y = 0.0;
		double r = 0.0;
		Set<POI> pois = new HashSet<POI>();
		for(int i=0; i < nodes.size(); i++ ){
			IClusterNode node = nodes.get(i);
			double x = node.getXAxis();
			double y = node.getYAxis();
			if(i == 0){
				max_x = x;
				min_x = x;
				max_y = y;
				min_y = y;
			} else {
				if(x > max_x)
					max_x = x;
				if(x < min_x)
					min_x = x;
				if(y > max_y)
					max_y = y;
				if(y < min_y)
					min_y = y;
			}
		}
		
		if(area_type.equals(Constants.CIRCLE_AREA)){
			area = new Circle();
			center_x = (max_x + min_x)/2;
			center_y = (max_y + min_y)/2;
			//double temp1 = max_x - center_x;
			//double temp2 = max_y - center_y;
			//r = Math.sqrt(temp1 * temp1 + temp2 * temp2);			
			//((Circle)area).setRadius(getDistance(max_x, max_y, min_x, min_y));
			((Circle)area).setCenterXAxis(center_x);
			((Circle)area).setCenterYAxis(center_y);
			((Circle)area).setMaxX(max_x);
			((Circle)area).setMaxY(max_y);
		} else {
			area = new Area();
		}
		for(IClusterNode node : nodes){
			pois.add((POI)node);
		}
		area.setNodes(pois);
		return area;
	}
	
	public ArrayList<Area> getNodeAreas(ArrayList<ArrayList<IClusterNode>> nodeClusters, String area_type){
		ArrayList<Area> result = new ArrayList<Area>();
		for(ArrayList< IClusterNode> nodesCluster : nodeClusters){
			Area area = this.getNodeArea(nodesCluster, area_type);
			if(area != null)
				result.add(area);
		}
		return result;
	}
	
	public ArrayList<ArrayList<IClusterNode>> getNodeClusters(List<? extends IClusterNode> clusterNodes, int k){
		 ArrayList<Node> nodes = new ArrayList<Node>();
		 for(int i=0; i<clusterNodes.size(); i++){
			nodes.add(new Node(clusterNodes.get(i)));
		 }
		 ArrayList<POIDistance> distances = new ArrayList<POIDistance>();
		 for(int i=0; i<nodes.size() - 1; i++){
			for(int j=i+1; j<nodes.size(); j++){
				distances.add(new POIDistance(nodes.get(i), nodes.get(j)));
			}
		 }
		 return this.getClusterWorker().clusterNodes(distances, nodes, k);
	}

}
